package com.ulife.order.components.core.refund;

import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.google.common.base.Strings;
import com.ulife.base.entities.order.*;
import com.ulife.base.entities.order.SaleOrderPay;
import com.ulife.base.logics.order.SaleOrderRefundLogic;
import com.ulife.base.logics.order.SoRefundBillLogic;
import com.ulife.common.utils.lang.JsonMapper;
import com.ulife.order.api.core.refund.OperationType;
import com.ulife.order.api.core.refund.RefundBillStatus;
import com.ulife.order.api.core.refund.RefundStatus;
import com.ulife.order.api.core.refund.RefundWay;
import com.ulife.order.components.core.pay.OrderPay;
import com.ulife.order.components.core.result.BaseResult;
import com.ulife.order.service.domain.Order;
import com.ulife.order.service.enumeration.PayStatus;

import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * Created by Lbq on 2016/05/24.
 */
@Service
public class OrderRefund {

    private static JsonMapper Json = JsonMapper.nonDefaultMapper();
    Logger logger = LoggerFactory.getLogger(Order.class);

    @Resource
    SoRefundBillLogic soRefundBillLogic;
    @Resource
    SaleOrderRefundLogic saleOrderRefundLogic;
    @Resource
    OrderPay orderPay;
    /*
    * 1.根据“订单取消单据”生成“退款单”
    *
    * 2.根据取消单据和支付记录
    *
    * 2.处理“退款单”
    *
    * */

    /*
    * 执行退款
    * 1.每个渠道的退款方式不一样
    * 2.考虑自动退款
    * 3.根据退款单退款状态更新退款单状态
    * 4.退款单状态变更触发订单的支付状态（暂时不回写）
    * */
    /**
     * 生成退款单号
     * @return
     * @throws Exception
     */
    private String getRefundBillNo() throws Exception {
        SoRefundBillCriteria soRefundBillCriteria = new SoRefundBillCriteria();
        String prefix = "RBN";
        String refundBillNo = "";
        int iTryCount = 0 ;
        while (iTryCount < 20){
            String randomString = UUID.randomUUID().toString().replaceAll("-", "");
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            refundBillNo = prefix + format.format(new Date()) + randomString.substring(8, 26);
            soRefundBillCriteria.clear();
            soRefundBillCriteria.createCriteria()
                    .andRefundBillNoEqualTo(refundBillNo);
            List<SoRefundBill> soRefundBills = soRefundBillLogic.list(soRefundBillCriteria);
            if(soRefundBills == null || soRefundBills.size() == 0){
                break;
            }
            if(iTryCount == 20){
                throw new Exception("生成退款单号出错！");
            }
            iTryCount++;
        }
        return refundBillNo;
    }

    /**
     * 创建退款单
     * @param soPay
     * @param refundWay
     * @param refundChannel
     * @return
     */
    public boolean createSoRefundBill(SaleOrderPay soPay ,OperationType operationType
            ,RefundWay refundWay, String refundChannel, long operator) throws Exception{

        //如果不是支付成功状态 返回
        if (soPay.getStatus() != PayStatus.Success.value()) {
            return false;
        }
        SoRefundBillCriteria soRefundBillCriteria = new SoRefundBillCriteria();
        soRefundBillCriteria.createCriteria()
                .andSoNoEqualTo(soPay.getSoNo())
                .andSoPayIdEqualTo(soPay.getId());
        List<SoRefundBill> soRefundBills = soRefundBillLogic.list(soRefundBillCriteria);
        if (soRefundBills != null && soRefundBills.size() > 0) //已存在退款单据
            return true;

        if(refundWay == null){
            refundWay = RefundWay.PayWayRefund;
        }
        if(operationType == null){
            operationType = OperationType.Manual;
        }
        if(Strings.isNullOrEmpty(refundChannel)){
            refundChannel = soPay.getPayChannel();
        }

        try{
                SoRefundBill soRefundBill = new SoRefundBill();

                soRefundBill.setRefundBillNo(getRefundBillNo());
                soRefundBill.setSoNo(soPay.getSoNo());
                soRefundBill.setRefundType(operationType.value());
                soRefundBill.setSoPayId(soPay.getId());
                soRefundBill.setRefundWay(refundWay.toString());
                soRefundBill.setRefundChannel(refundChannel);
                soRefundBill.setRefundStatus(RefundStatus.Created.value());
                soRefundBill.setRefundAmount(soPay.getAmount());
                soRefundBill.setStatus(RefundBillStatus.Created.value());
                soRefundBill.setCreateTime(new Date());
                soRefundBill.setCreator(operator);

                if(soRefundBillLogic.insertSelective(soRefundBill) == 1) return true;

        }catch (Exception ex){
            throw ex;
        }
        return false;
    }

    /**
     * 批量创建退款单
     * @param saleOrderPays
     * @param operationType
     * @param refundWay
     * @param refundChannel
     * @return
     */
    public boolean batchCreateSoRefundBill(List<SaleOrderPay> saleOrderPays
            , OperationType operationType
            , RefundWay refundWay, String refundChannel, long operator) throws Exception{
        try {
            //创建退款单
            for (SaleOrderPay soPay : saleOrderPays) {
                if (!createSoRefundBill(soPay, operationType, refundWay, refundChannel, operator)) {
                    return false;
                }
            }
            return true;
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * 根据状态获取订单退款单据
     * @param criteria 查询条件
     * @param rows 行数
     * @param page 页码
     * @return
     */
    public List<SoRefundBill> getRefundBillByCriteria(SoRefundBillCriteria criteria, int page, int rows) throws Exception{

        try {
            RowBounds rowBounds = new RowBounds((Math.max(1, page) - 1) * rows, rows);
            return soRefundBillLogic.list(criteria, rowBounds);
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * 根据退款状态更新订单取消单据状态
     * @param soRefundBill
     * @param refundStatus
     * @return
     */
    public boolean transRefundStatus(SoRefundBill soRefundBill ,RefundStatus refundStatus,long operator) throws Exception{

        if (soRefundBill == null || refundStatus == null || refundStatus != RefundStatus.Succeed)
            return false;

        try {
            SaleOrderRefundCriteria saleOrderRefundCriteria = new SaleOrderRefundCriteria();
            saleOrderRefundCriteria.createCriteria().andSoNoEqualTo(soRefundBill.getSoNo())
                    .andPayIdEqualTo(soRefundBill.getSoPayId());
            List<SaleOrderRefund> saleOrderRefunds = saleOrderRefundLogic.list(saleOrderRefundCriteria);
            if (saleOrderRefunds != null && saleOrderRefunds.size() > 0) {
                SaleOrderRefund saleOrderRefund = saleOrderRefunds.get(0);
                saleOrderRefund.setStatus(RefundStatus.Succeed.value());
                saleOrderRefund.setModifiedBy(Long.toString(operator));
                saleOrderRefund.setModifiedTime(new Date());
                saleOrderRefundLogic.updateSelective(saleOrderRefund);
            } else {
                SaleOrderRefund saleOrderRefund = new SaleOrderRefund();
                saleOrderRefund.setSoNo(soRefundBill.getSoNo());
                saleOrderRefund.setPayChannel(soRefundBill.getRefundChannel());
                saleOrderRefund.setPayId(soRefundBill.getSoPayId());
                saleOrderRefund.setAmount(soRefundBill.getRefundAmount());
                saleOrderRefund.setCreatedBy(Long.toString(operator));
                saleOrderRefund.setCreatedTime(new Date());
                saleOrderRefund.setModifiedBy(Long.toString(operator));
                saleOrderRefund.setModifiedTime(new Date());
                saleOrderRefund.setStatus(RefundStatus.Succeed.value());
                saleOrderRefundLogic.insertSelective(saleOrderRefund);
            }
            soRefundBill.setStatus(RefundBillStatus.Finished.value());
            soRefundBill.setRefundStatus(refundStatus.value());
            soRefundBill.setModifier(operator);
            soRefundBill.setModifyTime(new Date());
            soRefundBillLogic.updateSelective(soRefundBill);
        } catch (Exception ex) {
            throw ex;
        }
        return true;
    }

    /**
     * 更新单据状态
     * @param soRefundBill
     * @param refundBillStatus
     * @return
     */
    public boolean updateBillStatus(SoRefundBill soRefundBill,RefundBillStatus refundBillStatus) throws Exception{

        if(soRefundBill == null || refundBillStatus == null)
            return false;

        soRefundBill.setStatus(refundBillStatus.value());
        try{
            soRefundBillLogic.updateSelective(soRefundBill);
        }catch (Exception ex){
            throw ex;
        }
        return true;
    }

    /**
     * 执行退款
     * @param soRefundBill
     * @return
     * @throws Exception
     */
    public BaseResult<RefundStatus> doRefund(SoRefundBill soRefundBill,long operator) throws Exception {
        RefundManager refundManager = RefundManager.getInstance();

        //处理退款请求
        BaseResult<RefundStatus> result = refundManager.processRefund(soRefundBill,operator);
        //更新退款请求状态 + 退款单维护
        if (result.getData() == RefundStatus.Succeed) {
            transRefundStatus(soRefundBill, result.getData(),operator);
        }
        return result;
    }

    /*
    * 自动退款实现和注意事项
    * 1.在domain.Schedule 实现按照渠道获取退款单据列表
    * 2.处理退款
    * 3.回写退款信息
    * 4.在OrderSerice中实现退款
    * 5.配置计划任务
    * */
}